/*-------------------------------------------------------------------------
 *
 * itup.h
 *	  POSTGRES 索引元组的定义
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/itup.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef ITUP_H
#define ITUP_H

#include "access/tupdesc.h"
#include "access/tupmacs.h"
#include "storage/bufpage.h"
#include "storage/itemptr.h"

/*
 * 索引元组头结构
 *
 * 所有索引元组以 IndexTupleData 开头。如果 HasNulls 位被设置，则紧随其后的是 IndexAttributeBitMapData。索引属性值从一个 MAXALIGN 边界开始。
 *
 * 注意，为位图分配的空间大小不随属性数量变化；这是因为我们没有空间在头部存储属性数量。考虑到 MAXALIGN 约束，无论通常的 INDEX_MAX_KEYS 值如何，都没有任何节省空间的方式。
 */

typedef struct IndexTupleData
{
	ItemPointerData t_tid;		/* 引用 TID 到堆元组 */

	/* ---------------
	 * t_info 的布局如下：
	 *
	 * 第15位（高位）：有空值
	 * 第14位：有可变宽度属性
	 * 第13位：AM定义的含义
	 * 第12-0位：元组的大小
	 * ---------------
	 */

	unsigned short t_info;		/* 关于元组的各种信息 */

} IndexTupleData;				/* 结构结尾还跟随更多数据 */

typedef IndexTupleData *IndexTuple;

typedef struct IndexAttributeBitMapData
{
	bits8		bits[(INDEX_MAX_KEYS + 8 - 1) / 8];
}			IndexAttributeBitMapData;

typedef IndexAttributeBitMapData * IndexAttributeBitMap;

/*
 * t_info 操作宏
 */
#define INDEX_SIZE_MASK 0x1FFF
#define INDEX_AM_RESERVED_BIT 0x2000	/* 保留给索引-AM 特定
										 * 用途 */
#define INDEX_VAR_MASK	0x4000
#define INDEX_NULL_MASK 0x8000

#define IndexTupleSize(itup)		((Size) ((itup)->t_info & INDEX_SIZE_MASK))
#define IndexTupleHasNulls(itup)	((((IndexTuple) (itup))->t_info & INDEX_NULL_MASK))
#define IndexTupleHasVarwidths(itup) ((((IndexTuple) (itup))->t_info & INDEX_VAR_MASK))


/*
 * 接受一个 infomask 作为参数（主要因为这需要在 index_form_tuple 时可用，以便分配足够的空间）。
 */
#define IndexInfoFindDataOffset(t_info) \
( \
	(!((t_info) & INDEX_NULL_MASK)) ? \
	( \
		(Size)MAXALIGN(sizeof(IndexTupleData)) \
	) \
	: \
	( \
		(Size)MAXALIGN(sizeof(IndexTupleData) + sizeof(IndexAttributeBitMapData)) \
	) \
)

/* ----------------
 *		index_getattr
 *
 *		此方法调用频繁，因此我们将可缓存和 NULL 查询宏化，对于其他情况调用 nocache_index_getattr()。
 *
 * ----------------
 */
#define index_getattr(tup, attnum, tupleDesc, isnull) \
( \
	AssertMacro(PointerIsValid(isnull) && (attnum) > 0), \
	*(isnull) = false, \
	!IndexTupleHasNulls(tup) ? \
	( \
		TupleDescAttr((tupleDesc), (attnum)-1)->attcacheoff >= 0 ? \
		( \
			fetchatt(TupleDescAttr((tupleDesc), (attnum)-1), \
			(char *) (tup) + IndexInfoFindDataOffset((tup)->t_info) \
			+ TupleDescAttr((tupleDesc), (attnum)-1)->attcacheoff) \
		) \
		: \
			nocache_index_getattr((tup), (attnum), (tupleDesc)) \
	) \
	: \
	( \
		(att_isnull((attnum)-1, (char *)(tup) + sizeof(IndexTupleData))) ? \
		( \
			*(isnull) = true, \
			(Datum)NULL \
		) \
		: \
		( \
			nocache_index_getattr((tup), (attnum), (tupleDesc)) \
		) \
	) \
)

/*
 * MaxIndexTuplesPerPage 是可以适应一个索引页面的元组数量的上限。一个索引元组必须具有数据或 null 位图，因此我们可以安全地假设它至少比一个简单的 IndexTupleData 结构大 1 字节。我们之所以得到这个除数，是因为每个元组必须最大对齐，并且必须有一个相关的行指针。
 *
 * 为了保持索引类型无关性，这没有考虑页面上的任何特殊空间，因此是保守的。
 *
 * 注意：在 B 树非叶子页面中，第一个元组没有键（隐含为负无穷），因此打破了“至少比 1 字节大”的假设。在这样的页面上，N 个元组可能比此处估计的占用少一个 MAXALIGN 的量，从而似乎允许一个以上的元组。但这样的页面始终至少有 MAXALIGN 的特殊空间，所以我们是安全的。
 */
#define MaxIndexTuplesPerPage	\
	((int) ((BLCKSZ - SizeOfPageHeaderData) / \
			(MAXALIGN(sizeof(IndexTupleData) + 1) + sizeof(ItemIdData))))


/* 在 indextuple.c 中的例程 */
extern IndexTuple index_form_tuple(TupleDesc tupleDescriptor,
								   Datum *values, bool *isnull);
extern IndexTuple index_form_tuple_context(TupleDesc tupleDescriptor,
										   Datum *values, bool *isnull,
										   MemoryContext context);
extern Datum nocache_index_getattr(IndexTuple tup, int attnum,
								   TupleDesc tupleDesc);
extern void index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor,
							   Datum *values, bool *isnull);
extern void index_deform_tuple_internal(TupleDesc tupleDescriptor,
										Datum *values, bool *isnull,
										char *tp, bits8 *bp, int hasnulls);
extern IndexTuple CopyIndexTuple(IndexTuple source);
extern IndexTuple index_truncate_tuple(TupleDesc sourceDescriptor,
									   IndexTuple source, int leavenatts);

#endif							/* ITUP_H */
